package com.theeviljames.syntax;

import java.util.ArrayList;
import java.util.Stack;

import com.theeviljames.lexer.Lexer;
import com.theeviljames.lexer.PGrammar;
import com.theeviljames.lexer.Token;
import com.theeviljames.syntax.exceptions.SyntaxException;

public class SyntaxAnalyser {

	private Stack<Token> stack = new Stack<Token>();
	private Stack<ASTNode> ast = new Stack<ASTNode>();
	private Token current;
	private Lexer lex;
	private PGrammar grammar;
	private double[][][] variables;
	
	public SyntaxAnalyser(String expression) throws SyntaxException {
		lex = new Lexer(expression);
		//this.variables = variables;
	}

	
	public ASTNode parse() throws SyntaxException{
		grammar = new PGrammar();
		current = lex.nextToken();
		stack.push(current);
		ast.push(new ASTNode(current.getLiteral(),(current.getType()==Token.NONTERMINAL)?ASTNode.NONTERMINAL:ASTNode.TERMINAL,current.getType()));
		current = lex.nextToken();
		System.out.println("Stack> " + stack);
		System.out.println("Current> " + current);
		while(current.getType()!=Token.EOF){
			/*String a,b;
			if(stack.peek().getType()==Token.CONSTANT){
				a = "CONSTANT";
			}
			else if(stack.peek().getType()==Token.IDENTIFIER){
				a = "IDENTIFIER";
			}
			else{
				a = stack.peek().getLiteral();
			}
			if(current.getType()==Token.CONSTANT){
				b = "CONSTANT";
			}
			else if(current.getType()==Token.IDENTIFIER){
				b = "IDENTIFIER";
			}
			else{
				b = current.getLiteral();
			}*/
			int relationship = grammar.precedence(stack.peek(), current);
			System.out.println("Precedence >" + relationship + " between>" + stack.peek() + ", " + current);
			switch(relationship){
			case -2:
				throw new SyntaxException("Syntax error in input");
			case 1:
				reduce();
				break;
			case 0:
				shift();
				break;
			case -1:
				shift();
				break;
	
			}
			System.out.println("Stack> " + stack);
			System.out.println("Current> " + current);
		}
		while(stack.size()>1||!stack.peek().equals(grammar.getStartingSymbol())){
			reduce();
			System.out.println("Stack> " + stack);
			System.out.println("Current> " + current);
		}
		System.out.println("Sentential form of grammar successfully parsed!");
		return ast.pop();
	}
	private void shift(){
		stack.push(current);
		ast.push(new ASTNode(current.getLiteral(),(current.getType()==Token.NONTERMINAL)?ASTNode.NONTERMINAL:ASTNode.TERMINAL,current.getType()));
		current = lex.nextToken();
	}
	
	private void reduce() throws SyntaxException{
		ArrayList<Token> rhs = new ArrayList<Token>();
		//rhs.add(current);
		rhs.add(0,stack.pop());
		int p = 0;
		if(stack.size()!=0)p = grammar.precedence(stack.peek(), rhs.get(0));
		System.out.println(p);
		while(stack.size()!=0&&p!=-1){
			rhs.add(0,stack.pop());
			if(stack.size()!=0)p = grammar.precedence(stack.peek(), rhs.get(0));
		}
		//current = stack.pop();
		//rhs.add(current);
		//System.out.println(rhs);
		Token t = grammar.matchRHS(rhs);
		if(t==null)throw new SyntaxException("Parse failed");
		ASTNode n = new ASTNode(t.getLiteral(),(t.getType()==Token.NONTERMINAL)?ASTNode.NONTERMINAL:ASTNode.TERMINAL,t.getType());
		for(Token x:rhs){
			n.addASTNode(ast.pop());
		}
		stack.push(t);
		ast.push(n);
	}
}
